
#include "JsonClassGenerator.h"
#include "util.h"

using namespace codegen;
using namespace resip;
using namespace std;

#define RESIPROCATE_SUBSYSTEM LogSystem::LOGMODULE


JsonClassGenerator::JsonClassGenerator(neb::CJsonObject* json,
    const resip::Data& class_name,
    const resip::Data& base_class /*= resip::Data::Empty*/)
    : Generator(class_name, base_class)
    , root_(new JsonNodeRoot(Node::VT_Object, class_name, this))
    , json_(json)
{
}

JsonClassGenerator::~JsonClassGenerator()
{
    delete json_;
}

bool JsonClassGenerator::parse()
{
    return parse_recursive(root_, *json_);
}

bool JsonClassGenerator::parse_recursive(Node* parent, neb::CJsonObject& obj)
{
    std::string key;
    while (obj.GetKey(key))
    {
        Node* node = create_non_array_node(obj, key, parent);

        if (node->isCompound())
        {
            if (!parse_recursive(node, obj[key]))
            {
                return false;
            }
        }
    }

    if (obj.IsArray())
    {
        int size = obj.GetArraySize();
        if (size == 0)
        {
            add_error("Empty array are not allowed, likely \"arr\": []", parent);
            return false;
        }

        for (int i = 0; i < size; ++i)
        {
            Node* node = create_array_node(obj, i, parent);

            if (node->isObject())
            {
                if (!parse_recursive(node, obj[i]))
                {
                    return false;
                }
            }
            else if (node->isArray())
            {
                add_error("Nested array are not allowed, likely \"arrarr\": [[]]", parent);
                return false;
            }
        }
    }

    return true;
}

EncodeStream& JsonClassGenerator::encode_header(EncodeStream& ostm) const
{
    Generator::encode_header(ostm);
    ostm << Symbols::LF;

    if(root_->has_base_class())
        ostm << "#include \"JsonObjectBase.hxx\"" << Symbols::LF; 
    else
        ostm << "#include \"CJsonObject.hpp\"" << Symbols::LF;

    ostm << "#include <string>" << Symbols::LF
        << "#include <vector>" << Symbols::LF << Symbols::LF;
    return ostm;
}

EncodeStream& JsonClassGenerator::encode_class(EncodeStream& ostm) const
{
    root_->encode(ostm);
    return ostm;
}

EncodeStream& JsonClassGenerator::encode_footer(EncodeStream& ostm) const
{
    return Generator::encode_footer(ostm);
}

Node* JsonClassGenerator::create_array_node(const neb::CJsonObject& obj, int index, Node* parent)
{
    Node* node = 0;

    // the key is the parent array node key
    std::string key(parent->key_.c_str());

    int32_t i32;
    uint32_t ui32;
    int64_t i64;
    uint64_t ui64;
    bool b;
    float f;
    double d;
    std::string s;
    neb::CJsonObject o;
    if (obj.Get(index, i32))
    {
        node = new_node(Node::VT_Int, key.c_str(), resip::Data(i32), parent);
    }
    else if (obj.Get(index, ui32))
    {
        node = new_node(Node::VT_UInt, key.c_str(), resip::Data(ui32), parent);
    }
    else if (obj.Get(index, i64))
    {
        node = new_node(Node::VT_Int64, key.c_str(), resip::Data((double)i64), parent);
    }
    else if (obj.Get(index, ui64))
    {
        node = new_node(Node::VT_UInt64, key.c_str(), resip::Data((UInt64)ui64), parent);
    }
    else if (obj.Get(index, b))
    {
        node = new_node(Node::VT_Bool, key.c_str(), resip::Data(b), parent);
    }
    else if (obj.Get(index, f))
    {
        node = new_node(Node::VT_Float, key.c_str(), resip::Data(f), parent);
    }
    else if (obj.Get(index, d))
    {
        node = new_node(Node::VT_Double, key.c_str(), resip::Data(d), parent);
    }
    else if (obj.Get(index, s))
    {
        node = new_node(Node::VT_String, key.c_str(), resip::Data(s), parent);
    }
    else if (obj.Get(index, o))
    {
        if (o.IsArray())
        {
            node = new_node(Node::VT_Array, key.c_str(), resip::Data::Empty, parent);
        }
        else
        {
            node = new_node(Node::VT_Object, key.c_str(), resip::Data::Empty, parent);
        }
    }
    else
    {
        resip_assert(false);
    }

    return node;
}

Node* JsonClassGenerator::create_non_array_node(const neb::CJsonObject& obj, const std::string& key, Node* parent)
{
    Node* node = 0;

    int32_t i32;
    uint32_t ui32;
    int64_t i64;
    uint64_t ui64;
    bool b;
    float f;
    double d;
    std::string s;
    neb::CJsonObject o;
    if (obj.Get(key, i32))
    {
        node = new_node(Node::VT_Int, key.c_str(), resip::Data(i32), parent);
    }
    else if (obj.Get(key, ui32))
    {
        node = new_node(Node::VT_UInt, key.c_str(), resip::Data(ui32), parent);
    }
    else if (obj.Get(key, i64))
    {
        node = new_node(Node::VT_Int64, key.c_str(), resip::Data((double)i64), parent);
    }
    else if (obj.Get(key, ui64))
    {
        node = new_node(Node::VT_UInt64, key.c_str(), resip::Data((UInt64)ui64), parent);
    }
    else if (obj.Get(key, b))
    {
        node = new_node(Node::VT_Bool, key.c_str(), resip::Data(b), parent);
    }
    else if (obj.Get(key, f))
    {
        node = new_node(Node::VT_Float, key.c_str(), resip::Data(f), parent);
    }
    else if (obj.Get(key, d))
    {
        node = new_node(Node::VT_Double, key.c_str(), resip::Data(d), parent);
    }
    else if (obj.Get(key, s))
    {
        node = new_node(Node::VT_String, key.c_str(), resip::Data(s), parent);
    }
    else if (obj.Get(key, o))
    {
        if (o.IsArray())
        {
            node = new_node(Node::VT_Array, key.c_str(), resip::Data::Empty, parent);
        }
        else
        {
            node = new_node(Node::VT_Object, key.c_str(), resip::Data::Empty, parent);
        }
    }
    else
    {
        resip_assert(false);
    }

    return node;
}

Node* JsonClassGenerator::new_node(
    Node::Type type,
    const resip::Data& key,
    const resip::Data& val,
    Node* parent)
{
    JsonNode* node = new JsonNode(type, key, val, parent, this);
    parent->add_child(node);
    return node;
}

